package com.wangtianxiang.sql.generator.loader.template;

import com.wangtianxiang.sql.generator.domain.SqlFile;
import com.wangtianxiang.sql.generator.utils.FileUtils;
import com.wangtianxiang.sql.generator.Generator;
import com.wangtianxiang.sql.generator.constants.GeneratorConstants;
import com.wangtianxiang.sql.generator.domain.SqlTemplate;
import com.wangtianxiang.sql.generator.utils.StringUtils;
import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XmlTemplateSourceLoader extends AbstractTemplateSourceLoader {

    private final List<SqlFile> sqlFiles = new ArrayList<>();

    private final String DATA_EXPRESSION_REG = "\\$\\{([^}]*)\\}";


    public XmlTemplateSourceLoader(String path) {
        super(path);
    }

    @Override
    public List<SqlFile> load(Map<String,Object> dataContext) {
        List<File> files = FileUtils.listFile(Generator.class.getClassLoader().getResource(this.getPath()).getPath(), "xml");
        for (File file : files) {
            doLoad(file,dataContext);
        }
        return sqlFiles;
    }

    private Map<String, SqlTemplate> doLoad(File file,Map<String,Object> dataContext){
        Map<String,SqlTemplate> sqlTemplateMap = new LinkedHashMap<>();
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(file);
            if(null != document){
                Element rootElement = document.getRootElement();
                parseXml(dataContext,rootElement,sqlTemplateMap);
                SqlFile sqlFile = new SqlFile();
                String outPath = rootElement.attributeValue(GeneratorConstants.ATTRIBUTE_OUT_PATH);
                sqlFile.setOutPutPath(outPath);
                sqlFile.getSqlTemplateMap().putAll(sqlTemplateMap);
                sqlFiles.add(sqlFile);
            }
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
        return sqlTemplateMap;
    }

    private void parseXml(Map<String,Object> dataContext,Element rootElement,Map<String,SqlTemplate> sqlTemplateMap){
        if(rootElement.nodeCount() > 0){
            Iterator<Element> it = rootElement.elementIterator(GeneratorConstants.NODE_SQL_TEMPLATE);
            while(it.hasNext()){
                parseTemplate(dataContext,it.next(),sqlTemplateMap);
            }
        }

    }

    private void parseTemplate(Map<String,Object> dataContext,Element templateElement,Map<String,SqlTemplate> sqlTemplateMap){
        SqlTemplate sqlTemplate = new SqlTemplate();
        sqlTemplate.setTemplateId(templateElement.attribute(GeneratorConstants.ATTRIBUTE_SQL_TEMPLATE_ID).getValue());

        doParseTemplate(dataContext,templateElement,sqlTemplate);
        sqlTemplateMap.put(sqlTemplate.getTemplateId(),sqlTemplate);
    }

    private void doParseTemplate(Map<String,Object> dataContext,Element templateElement,SqlTemplate sqlTemplate) {
        if (templateElement.nodeCount() > 0) {
            Iterator<Node> nodes = templateElement.nodeIterator();
            while(nodes.hasNext()){
                Node child = nodes.next();
                String text = child.getText();
//                System.out.println("nodeName: " + child.getName());
//                System.out.println("nodeType: " + child.getNodeType());
//                System.out.println("nodeTypeName: " + child.getNodeTypeName());
//                System.out.println("nodeText:" + text);
                if (child.getNodeType() == 1) {//Element

                    switch (child.getName()){
                        case GeneratorConstants.NODE_FOREACH: parseNodeForeach(dataContext,child,sqlTemplate);break;
                    }

                } else if(child.getNodeType() == 3) {//Text
                    sqlTemplate.getSql().append(text);
                }
            }

        }
    }



    private String getDataExpression(String content){
        Pattern regex = Pattern.compile(DATA_EXPRESSION_REG);
        Matcher matcher = regex.matcher(content);
        StringBuilder sql = new StringBuilder();
        while(matcher.find()) {
            sql.append(matcher.group(1)+",");
        }
        if (sql.length() > 0) {
            sql.deleteCharAt(sql.length() - 1);
        }
        return sql.toString();
    }

    private Object parseDataExpressionValue(Map<String,Object> dataContext,String dataExpression){
        String[] keys = dataExpression.split("\\.");
        Object value = dataContext.get(keys[0]);
        for(int index = 1;index < keys.length;index++){
            if(value instanceof Map){
                value = ((Map<String,Object>)value).get(keys[index]);
            }else{
                break;
            }
        }


        return value;
    }



    private void parseNodeForeach(Map<String,Object> dataContext,Node node,SqlTemplate sqlTemplate){//传入foreach节点
        Element element = (Element) node;
        String itemPlace = element.attributeValue(GeneratorConstants.ATTRIBUTE_ITEM);
        String params = element.attributeValue(GeneratorConstants.ATTRIBUTE_COLLECTION);
        List<String> collection = new ArrayList<>();
        String dataExpression = getDataExpression(params);
        if(StringUtils.isNotEmpty(dataExpression)){
            //解析数据
            Object value = parseDataExpressionValue(dataContext,dataExpression);
            if(value instanceof String){
                collection = Arrays.asList(((String)value).split(","));
            }else if(value instanceof List){
                collection = (List<String>) value;
            }
        }else{
            collection = Arrays.asList(params.split(","));
        }
        for (String item : collection) {
            //获取所有子节点
            if(element.nodeCount() > 0){
                Iterator<Node> childNodes = element.nodeIterator();
                while(childNodes.hasNext()){
                    Node childNode = childNodes.next();
                    if(childNode.getNodeType() == 3){
                        sqlTemplate.getSql().append(childNode.getText().replace(String.format("#{%s}", itemPlace), item));
                    }else if(childNode.getNodeType() == 1){
                        parseNodeForeach(dataContext,childNode,sqlTemplate);
                    }

                }
            }
        }
    }
}
